using Glpt.Data;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;

namespace Glpt.Task
{
    /// <summary>
    /// FTP的连接类
    /// </summary>
    public class FtpServerInfo
    {
        /// <summary>
        /// FTP类别
        /// </summary>
        public string FtpType = "";
        /// <summary>
        /// IP地址或DNS
        /// </summary>
        public string Host = "";
        /// <summary>
        /// 端口
        /// </summary>
        public int Port = 0;
        /// <summary>
        /// 用户名
        /// </summary>
        public string Username = "";
        /// <summary>
        /// 密码
        /// </summary>
        public string Password = "";
        /// <summary>
        /// 路径
        /// </summary>
        public string Path = "";
        /// <summary>
        /// 数据传输过程的行为
        /// </summary>
        public bool UsePassive = true;
        /// <summary>
        /// 初始化类的新实例
        /// </summary>
        public FtpServerInfo()
        {
        }
        /// <summary>
        /// 初始化类的新实例
        /// </summary>
        /// <param name="url">URL</param>
        public FtpServerInfo(string url)
        {
            SetURL(url);
        }
        /// <summary>
        /// 设置URL
        /// </summary>
        /// <param name="url">URL</param>
        public void SetURL(string url)
        {
            string val = "";
            //格式说明：sftp://<用户名>:<密码>@<IP地址或DNS>:端口/pathname/filename.txt
            if (url.ToLower().StartsWith("sftp://"))
            {
                FtpType = "sftp";
                Port = 22;
                val = url.Substring(7);
            }
            else if (url.ToLower().StartsWith("ftp://"))
            {
                FtpType = "ftp";
                Port = 21;
                val = url.Substring(6);
            }
            else
                throw new Exception("参数格式错误");
            int idx = val.LastIndexOf("@");
            int idx2 = 0;
            if (idx > 0)
            {
                idx2 = val.IndexOf(":");
                if (idx2 <= 0)
                    throw new Exception("参数格式错误");
                Username = val.Substring(0, idx2);
                Password = val.Substring(idx2 + 1, idx - idx2 - 1);
                if (AbstractInterFace.PublicLeftChar.Length > 0 && AbstractInterFace.PublicRightChar.Length > 0 && Password.StartsWith(AbstractInterFace.PublicLeftChar) && Password.EndsWith(AbstractInterFace.PublicRightChar))
                {
                    CharCrypto crypt = new CharCrypto();
                    crypt.Init(AbstractInterFace.PublicLeftChar, AbstractInterFace.PublicRightChar);
                    Password = crypt.Decrypt(Password);
                }
                val = val.Substring(idx + 1);
            }
            idx = val.IndexOf("/");
            if (idx <= 0)
                idx = val.Length;
            else
                Path = val.Substring(idx);
            idx2 = val.IndexOf(":");
            if (idx2 > 0)
            {
                Host = val.Substring(0, idx2);
                Port = int.Parse(val.Substring(idx2 + 1, idx - idx2 - 1));
            }
            else
                Host = val.Substring(0, idx);
        }
        /// <summary>
        /// 获取URL
        /// </summary>
        /// <param name="has_user_pass">是否要包含用户名和密码</param>
        /// <returns>URL</returns>
        private string GetURL(bool has_user_pass)
        {
            return GetRoot(has_user_pass) + this.Path;
        }

        private string GetRoot(bool has_user_pass)
        {
            return FtpType + "://" + (has_user_pass ? this.Username + ":" + this.Password + "@" : "") + this.Host + ":" + this.Port.ToString();
        }

        /// <summary>
        /// 下载
        /// </summary>
        /// <param name="filename">存放文件的路径</param>
        public void DownloadFile(string filename)
        {
            if (FtpType.Equals("sftp"))//从sftp下载
            {
                Renci.SshNet.SftpClient sftp = GetSftpClient();
                FileInfo file = new FileInfo(filename);
                if (!file.Directory.Exists) file.Directory.Create();
                using (FileStream fs = file.OpenWrite())
                {
                    Connect(sftp);
                    try
                    {
                        sftp.DownloadFile(this.Path, fs);
                    }
                    finally
                    {
                        if (sftp.IsConnected)
                            sftp.Disconnect();
                    }
                }
            }
            else
            {
                FtpWebResponse response = null;
                Stream ns = null;
                FileInfo file = new FileInfo(filename);
                if (!file.Directory.Exists) file.Directory.Create();
                FileStream fs = null;
                try
                {
                    FtpWebRequest reqFTP = GetFtpWebRequest(GetURL(false));
                    reqFTP.Method = WebRequestMethods.Ftp.DownloadFile;
                    response = (FtpWebResponse)reqFTP.GetResponse();
                    ns = response.GetResponseStream();
                    int len = 5120;
                    byte[] nbytes = new byte[len];
                    int nReadSize = 0;
                    nReadSize = ns.Read(nbytes, 0, len);
                    fs = new FileStream(filename, FileMode.Create);
                    while (nReadSize > 0)
                    {
                        fs.Write(nbytes, 0, nReadSize);
                        nReadSize = ns.Read(nbytes, 0, len);
                    }
                }
                finally
                {
                    if (ns != null) ns.Close();
                    if (response != null) response.Close();
                    if (fs != null) fs.Close();
                }
            }
        }
        /// <summary>
        /// 上传
        /// </summary>
        /// <param name="filename">文件路径</param>
        public void UploadFile(string filename)
        {
            using (FileStream fs = File.OpenRead(filename))
            {
                UploadFile(fs);
            }
        }
        /// <summary>
        /// 上传
        /// </summary>
        /// <param name="filedata">文件</param>
        public void UploadFile(byte[] filedata)
        {
            using (MemoryStream fs = new MemoryStream(filedata))
            {
                UploadFile(fs);
            }
        }
        /// <summary>
        /// 上传
        /// </summary>
        /// <param name="streamdata">数据流</param>
        public void UploadFile(Stream streamdata)
        {
            if (FtpType.Equals("sftp"))//上传到sftp
            {
                Renci.SshNet.SftpClient sftp = GetSftpClient();
                Connect(sftp);
                try
                {
                    sftp.UploadFile(streamdata, this.Path);
                }
                finally
                {
                    if (sftp.IsConnected)
                        sftp.Disconnect();
                }
            }
            else
            {
                Stream ns = null;
                try
                {
                    string url = GetURL(false);
                    CheckCreateFtpDirectory(GetParentPath(url));
                    FtpWebRequest reqFTP = GetFtpWebRequest(url);
                    reqFTP.Method = WebRequestMethods.Ftp.UploadFile;
                    ns = reqFTP.GetRequestStream();
                    int len = 5120;
                    byte[] nbytes = new byte[len];
                    int nReadSize = 0;
                    nReadSize = streamdata.Read(nbytes, 0, len);
                    while (nReadSize > 0)
                    {
                        ns.Write(nbytes, 0, nReadSize);
                        nReadSize = streamdata.Read(nbytes, 0, len);
                    }
                }
                finally
                {
                    if (ns != null) ns.Close();
                }
            }
        }
        //获取上级路径
        public static string GetParentPath(string FtpFullName)
        {
            int idx = FtpFullName.LastIndexOf("/");
            if (idx > 7)
                return FtpFullName.Substring(0, idx);
            else
                return "";
        }
        /// <summary>
        /// 检测目录是否存在
        /// </summary>
        /// <returns>是否存在</returns>
        public bool CheckDirectoryExists()
        {
            string url = GetURL(false);
            if (url.Length == 0) return true;
            url = GetParentPath(url);
            if (url.Length == 0) return true;
            string path = GetParentPath(url);
            if (path.Length == 0) return true;
            string dirname = url.Substring(path.Length + 1);
            bool dirExists = false;
            //检测目录是否存在
            try
            {
                List<string> lst = GetFileList(path, dirname, SearchOption.TopDirectoryOnly, true);
                foreach (string val in lst)
                {
                    if (val.Equals(dirname))
                    {
                        dirExists = true;
                        break;
                    }
                }
            }
            catch
            {
                dirExists = true;//报错时不一定能确定目录不存在
            }
            return dirExists;
        }
        private void CheckCreateFtpDirectory(string url)
        {
            if (url.Length == 0) return;
            string path = GetParentPath(url);
            if (path.Length == 0) return;
            string dirname = url.Substring(path.Length + 1);
            bool dirExists = false;
            //检测目录是否存在
            try
            {
                List<string> lst = GetFileList(path, dirname, SearchOption.TopDirectoryOnly, true);
                foreach (string val in lst)
                {
                    if (val.Equals(dirname))
                    {
                        dirExists = true;
                        break;
                    }
                }
            }
            catch
            {
                CheckCreateFtpDirectory(path);
            }
            if (!dirExists)//创建目录
            {
                try
                {
                    FtpWebRequest reqFTP = GetFtpWebRequest(url);
                    reqFTP.Method = WebRequestMethods.Ftp.MakeDirectory;
                    FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
                    response.Close();
                }
                catch { }
            }
        }

        public List<string> GetFileList()
        {
            return GetFileList(SearchOption.TopDirectoryOnly);
        }
        public List<string> GetFileList(SearchOption search_option)
        {
            string url = GetURL(false);
            string path = GetParentPath(url);
            string searchPattern = (path.Length == 0 ? "*" : url.Substring(path.Length + 1));
            if (searchPattern.Length == 0) searchPattern = "*";
            return GetFileList(path, searchPattern, search_option, false);
        }

        private void Connect(Renci.SshNet.SftpClient sftp)
        {
            ///////重做3次//////
            int iredo = 0;
            int iredomax = 3;
            while ((iredo++) < iredomax)
            {
                try
                {
                    sftp.Connect();
                    break;
                }
                catch (Exception ex)
                {
                    bool isredo = ex.Message.Equals("Permission denied (password).");
                    if (!isredo || iredo == iredomax)
                        throw;
                    System.Threading.Thread.Sleep(3000);
                }
            }
            ////////////////////
        }

        /// <summary>
        /// 获取文件列表
        /// </summary>
        /// <param name="path">路径</param>
        /// <param name="searchPattern">搜索字符串</param>
        /// <returns>文件列表</returns>
        private List<string> GetFileList(string path, string searchPattern, SearchOption search_option, bool isdir)
        {
            List<string> lst = new List<string>();
            if (FtpType.Equals("sftp"))
            {
                Renci.SshNet.SftpClient sftp = GetSftpClient();
                Connect(sftp);
                try
                {
                    string root = GetRoot(false);
                    if (root.Length >= path.Length)
                        path = "";
                    else
                        path = path.Substring(root.Length);
                    lst = GetSftpFileList(sftp, path, searchPattern, search_option, isdir);
                }
                finally
                {
                    if (sftp.IsConnected)
                        sftp.Disconnect();
                }
            }
            else
            {
                lst = GetFtpFileList(path, searchPattern, search_option, isdir);
            }
            return lst;
        }

        private List<string> GetSftpFileList(Renci.SshNet.SftpClient sftp, string path, string searchPattern, SearchOption search_option, bool isdir)
        {
            List<string> lst = new List<string>();
            var files = sftp.ListDirectory(path);
            foreach (Renci.SshNet.Sftp.SftpFile f in files)
            {
                string val = f.Name;
                if (val.Equals(".") || val.Equals(".."))
                    continue;
                if (f.IsDirectory)
                {
                    if (search_option.Equals(SearchOption.AllDirectories))
                    {
                        List<string> lst2 = GetSftpFileList(sftp, path + "/" + val, searchPattern, search_option, isdir);
                        foreach (string tmp2 in lst2)
                            lst.Add(val + "/" + tmp2);
                    }
                    if (!isdir)
                        continue;
                }
                if (IsMatch(val.ToLower(), searchPattern.ToLower()))
                    lst.Add(val);
            }
            return lst;
        }
        private List<string> GetFtpFileList(string path, string searchPattern, SearchOption search_option, bool isdir)
        {
            List<string> lst = new List<string>();
            FtpWebRequest reqFTP = GetFtpWebRequest(path);
            reqFTP.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
            FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
            using (StreamReader read = new StreamReader(response.GetResponseStream()))
            {
                string[] ls = read.ReadToEnd().Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
                response.Close();
                foreach (string tmp in ls)
                {
                    string[] vals = tmp.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                    string val = vals[vals.Length - 1];
                    if (vals.Length == 9 && vals[0].StartsWith("d") || vals.Length == 4 && vals[2].Equals("<DIR>"))
                    {
                        if (val.Equals(".") || val.Equals(".."))
                            continue;
                        if (search_option.Equals(SearchOption.AllDirectories))
                        {
                            List<string> lst2 = GetFtpFileList(path + "/" + val, searchPattern, search_option, isdir);
                            foreach (string tmp2 in lst2)
                                lst.Add(val + "/" + tmp2);
                        }
                        if (!isdir)
                            continue;
                    }
                    if (IsMatch(val.ToLower(), searchPattern.ToLower()))
                        lst.Add(val);
                }
            }
            return lst;
        }

        public bool IsMatch(string value, string regex)
        {
            if (regex == "*" || value == regex) return true;
            if (value == "" || regex == "") return false;
            if (value[0] == regex[0] || regex[0] == '?') return IsMatch(value.Substring(1), regex.Substring(1));
            if (regex[0] == '*') return IsMatch(value.Substring(1), regex) || IsMatch(value, regex.Substring(1));
            return false;
        }

        private FtpWebRequest GetFtpWebRequest(string url)
        {
            FtpWebRequest reqFTP = (FtpWebRequest)WebRequest.Create(url);
            reqFTP.Credentials = new NetworkCredential(this.Username, this.Password);
            reqFTP.UsePassive = this.UsePassive;
            reqFTP.UseBinary = true;
            return reqFTP;
        }
        private Renci.SshNet.SftpClient GetSftpClient()
        {
            Renci.SshNet.SftpClient sftp = null;
            if (this.Password.IndexOf(@":\") == 1)
            {
                Renci.SshNet.PrivateKeyFile file = new Renci.SshNet.PrivateKeyFile(this.Password);
                sftp = new Renci.SshNet.SftpClient(this.Host, this.Port, this.Username, file);
            }
            else
                sftp = new Renci.SshNet.SftpClient(this.Host, this.Port, this.Username, this.Password);
            return sftp;
        }
    }
}
